package com.atguigu.yygh.common.utils;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.junit.platform.commons.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 树仁信息科技
 */
public class HttpUntils {
    private static final Logger logger = LoggerFactory.getLogger(HttpUntils.class);

    /**
     * 默认Connection request超时时间 5 S
     */
    private static final int CONNECTION_REQUEST_TIMEOUT = 5 * 1000;
    /**
     * 默认连接超时时间 30 S
     */
    private static final int CONNECTION_TIMEOUT = 30 * 1000;
    /**
     * 默认获取数据的超时时间 80 S
     */
    public static final int SOCKET_TIMEOUT = 80 * 1000;

    /**
     * http成功状态码
     */
    private static final int HTTP_CODE_OK = 200;

    /**
     * 默认的编码格式
     */
    private static final String CHARSET = "UTF-8";

    public static String get(String url) {
        return get(url, SOCKET_TIMEOUT, CHARSET);
    }

    /**
     * 使用默认的超时时间45s 发起get请求
     *
     * @param url    请求地址
     * @param params 请求参数
     * @return 请求结果
     */
    public static String get(String url, Map<String, Object> params) {
        return get(url, params, SOCKET_TIMEOUT, CHARSET);
    }

    /**
     * 使用默认的超时时间45s 发起post请求
     *
     * @param url    请求地址
     * @param params 请求参数
     * @return 请求结果
     */
    public static String post(String url, Map<String, ?> params) {
        return post(url, params, SOCKET_TIMEOUT, CHARSET);
    }

    /**
     * 使用默认的超时时间45s 发起post请求 json参数
     *
     * @param url       请求地址
     * @param jsonParam 请求参数
     * @return 请求结果
     */
    public static String postJson(String url, String jsonParam) {
        System.out.println("jsonParam"+jsonParam);
        return postJson(url, jsonParam, null, SOCKET_TIMEOUT, CHARSET);
    }

    /**
     * 发起post请求 json参数
     *
     * @param url
     * @param jsonParam
     * @param timeout
     * @return
     */
    public static String postJson(String url, String jsonParam, int timeout) {
        return postJson(url, jsonParam, null, timeout, CHARSET);
    }

    /**
     * 使用默认的超时时间45s 发起post请求 json参数
     *
     * @param url          请求地址
     * @param jsonParam    请求参数
     * @param headerParams 请求头参数
     * @return 请求结果
     */
    public static String postJson(String url, Map<String, String> headerParams, String jsonParam) {
        return postJson(url, jsonParam, headerParams, SOCKET_TIMEOUT, CHARSET);
    }

    /**
     * 使用默认的超时时间45s 发起post请求 json参数
     *
     * @param url              请求地址
     * @param jsonParam        请求参数
     * @param headerParamKey   请求头部参数key
     * @param headerParamValue 请求头部参数value
     * @return 请求结果
     */
    public static String postJsonAndHeader(String url, String jsonParam, String headerParamKey, final String headerParamValue) {
        return postJsonAndHeader(url, jsonParam, headerParamKey, headerParamValue, SOCKET_TIMEOUT, CHARSET);
    }

    public static String get(final String url, int timeout, String charset) {
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpGet get = new HttpGet(url);
        return baseRequest(httpClient, get, timeout, charset);
    }

    /**
     * 发起get请求
     *
     * @param url     请求地址
     * @param params  请求参数
     * @param timeout 超时时间
     * @param charset 服务器端响应编码
     * @return 请求结果
     */
    public static String get(final String url, final Map<String, Object> params, int timeout, String charset) {
        StringBuilder uri = new StringBuilder(url);
        urlAppendParam(uri, params);
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpGet get = new HttpGet(uri.toString());
        return baseRequest(httpClient, get, timeout, charset);
    }

    public static void urlAppendParam(StringBuilder url, Map<String, Object> params) {
        if (null != params && !params.isEmpty()) {
            StringBuilder sb = new StringBuilder("?");
            for (String key : params.keySet()) {
                sb.append(key).append("=").append(params.get(key)).append("&");
            }
            url.append(sb.substring(0, sb.length() - 1));
        }
    }

    /**
     * @param url     请求地址
     * @param params  请求参数
     * @param timeout 超时时间
     * @param charset 服务器端响应编码
     * @return 请求结果
     */
    public static String post(final String url, final Map<String, ?> params, int timeout, String charset) {
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpPost post = new HttpPost(url);
        if (null != params && !params.isEmpty()) {
            List<NameValuePair> nvpList = new ArrayList<>();
            for (Map.Entry<String, ?> entry : params.entrySet()) {
                NameValuePair nvp = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
                nvpList.add(nvp);
            }
            post.setEntity(new UrlEncodedFormEntity(nvpList, Charset.forName(CHARSET)));
        }

        return baseRequest(httpClient, post, timeout, charset);
    }

    /**
     * https post请求 请求参数为json
     *
     * @param url          请求地址
     * @param jsonParam    请求参数
     * @param headerParams 请求头参数
     * @param timeout      超时时间
     * @param charset      服务器端响应编码
     * @return 请求结果
     */
    public static String postJson(final String url, final String jsonParam, Map<String, String> headerParams, int timeout, String charset) {
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpPost post = new HttpPost(url);
        StringEntity entity = new StringEntity(jsonParam, ContentType.create("application/json", charset));
        post.setEntity(entity);
        // 设置请求头
        if (headerParams != null) {
            Header[] headers = new Header[headerParams.size()];
            int i = 0;
            for (Map.Entry<String, String> entry : headerParams.entrySet()) {
                headers[i] = new BasicHeader(entry.getKey(), entry.getValue());
                ++i;
            }
            post.setHeaders(headers);
        }
        return baseRequest(httpClient, post, timeout, charset);
    }

    /**
     * https post请求 请求参数为json
     *
     * @param url              请求地址
     * @param jsonParam        请求参数
     * @param headerParamKey   请求头参数key
     * @param headerParamValue 请求头参数key
     * @param timeout          超时时间
     * @param charset          服务器端响应编码
     * @return 请求结果
     */
    public static String postJsonAndHeader(final String url, final String jsonParam, final String headerParamKey, final String headerParamValue, int timeout, String charset) {
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpPost post = new HttpPost(url);
        StringEntity entity = new StringEntity(jsonParam, ContentType.APPLICATION_JSON);
        post.setEntity(entity);
        post.addHeader(headerParamKey, headerParamValue);
        return baseRequest(httpClient, post, timeout, charset);
    }

    public static String postXml(final String url, final String xmlRequest) {
        return postXml(url, null, xmlRequest, SOCKET_TIMEOUT, CHARSET);
    }


    public static String postXml(final String url, final String xmlRequest, int timeout, String charset) {
        return postXml(url, null, xmlRequest, timeout, charset);
    }

    public static String postXml(final String url, final Map<String, Object> params, final String xmlRequest, int timeout, String charset) {
        CloseableHttpClient httpClient = createSSLClientDefault();
        StringBuilder urlBuf = new StringBuilder(url);
        if (params != null) {
            urlAppendParam(urlBuf, params);
        }
        HttpPost post = new HttpPost(urlBuf.toString());
        StringEntity entity = new StringEntity(xmlRequest, ContentType.create("application/xml", Charset.forName(charset)));
        post.setEntity(entity);
        return baseRequest(httpClient, post, timeout, charset);
    }

    /**
     * @param httpClient        http客户端
     * @param base              请求对象
     * @param conRequestTimeout 连接池获取超时时间
     * @param conTimeout        连接url超时时间
     * @param socketTimeout     数据获取超时时间
     * @param charset           服务器端响应编码
     * @return 请求结果
     */
    public static String baseRequest(CloseableHttpClient httpClient, HttpRequestBase base,
                                     int conRequestTimeout, int conTimeout, int socketTimeout, String charset) {
        CloseableHttpResponse response = null;
        String result = null;
        //设置超时时间
        base.setConfig(RequestConfig.custom()
                .setConnectionRequestTimeout(conRequestTimeout)
                .setConnectTimeout(conTimeout)
                .setSocketTimeout(socketTimeout)
                .build());
        try {
            response = httpClient.execute(base);
            if (response.getStatusLine().getStatusCode() == HTTP_CODE_OK) {
                HttpEntity entity = response.getEntity();
                if (null != entity) {
                    result = EntityUtils.toString(entity, charset);
                }
            }

            String log = result;
            if (StringUtils.isNotBlank(log) && log.length() > 1024) {
                log = log.substring(0, 1024);
            }

            logger.info("response code:{}, content:{}", response.getStatusLine().getStatusCode(), log);
        } catch (IOException ex) {
            logger.info(ex.getMessage(), ex);
        } finally {
            if (null != response) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException ex) {
                    logger.info(ex.getMessage(), ex);
                }
            }
        }
        return result;
    }

    /**
     * httpClient通用执行流程
     *
     * @param httpClient    http客户端
     * @param base          请求对象
     * @param socketTimeout 数据获取超时时间
     * @param charset       服务器端响应编码
     * @return 请求结果
     */
    public static String baseRequest(CloseableHttpClient httpClient, HttpRequestBase base,
                                     int socketTimeout, String charset) {
        return baseRequest(httpClient, base, CONNECTION_REQUEST_TIMEOUT, CONNECTION_TIMEOUT, socketTimeout, charset);
    }

    /**
     * 创建默认http客户端
     *
     * @return http客户端
     */
    private static CloseableHttpClient createSSLClientDefault() {

        SSLContext sslContext;
        try {
            sslContext = SSLContext.getDefault();

            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);

            return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();

        } catch (NoSuchAlgorithmException ex) {
            logger.info(ex.getMessage());
//            logger.info(ex.getMessage(), ex);
        }

        return HttpClients.createDefault();
    }

    /**
     * 中转文件
     *
     * @param file 上传的文件
     * @param url  第三方服务器请求地址
     * @return 响应结果
     */
    public static String httpClientUploadFile(MultipartFile file, String url, String source) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String result = "";
        try {
            String fileName = file.getOriginalFilename();
            HttpPost httpPost = new HttpPost(url);
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            // 文件流
            builder.addBinaryBody("file", file.getInputStream(), ContentType.MULTIPART_FORM_DATA, fileName);
            // 类似浏览器表单提交，对应input的name和value
            builder.addTextBody("source", source);
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            // 执行提交
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, Charset.forName("UTF-8"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return result;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 从网络Url中下载文件
     */
    public static void downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时间为3秒
        conn.setConnectTimeout(3 * 1000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //得到输入流
        InputStream inputStream = conn.getInputStream();
        //获取自己数组
        byte[] getData = readInputStream(inputStream);

        //文件保存位置
        File saveDir = new File(savePath);
        if (!saveDir.exists()) {
            saveDir.mkdir();
        }
        File file = new File(saveDir + File.separator + fileName);
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(getData);
        fos.close();
        if (inputStream != null) {
            inputStream.close();
        }
    }

    /**
     * 从输入流中获取字节数组
     */
    private static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    public static byte[] download(String downloadUrl) throws Exception {
        URL url = new URL(downloadUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时间为3秒
        conn.setConnectTimeout(3 * 1000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //得到输入流
        InputStream inputStream = conn.getInputStream();
        //获取自己数组
        byte[] data = readInputStream(inputStream);
        if (inputStream != null) {
            inputStream.close();
        }
        return data;
    }

    public static void main(String[] args) {
        String url = "https://as01.nxcloud.com/record/32bac790-795c-2c8b-2a6a-e128e4928dbf";
        getRedirectUrl(url);
    }

    /**
     * 获取重定向url
     *
     * @param url
     * @return
     */
    public static String getRedirectUrl(String url) {
        CloseableHttpClient httpClient = createNewSSLClientDefault();
        HttpGet get = new HttpGet(url);
        CloseableHttpResponse response = null;
        String result = null;
        //设置超时时间
        get.setConfig(RequestConfig.custom()
                .setConnectionRequestTimeout(SOCKET_TIMEOUT)
                .setConnectTimeout(CONNECTION_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .setRedirectsEnabled(false)
                .build());
        try {
            response = httpClient.execute(get);
            if (response.getStatusLine().getStatusCode() == 302) {
                result = response.getFirstHeader("Location").getValue();
            }
        } catch (IOException ex) {
            logger.info(ex.getMessage());
//            logger.info(ex.getMessage(), ex);
        } finally {
            if (null != response) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException ex) {
                    logger.info(ex.getMessage());
//                    logger.info(ex.getMessage(), ex);
                }
            }
        }
        return result;
    }

    public static CloseableHttpClient createNewSSLClientDefault() {
        try {
            //使用 loadTrustMaterial() 方法实现一个信任策略，信任所有证书
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            //NoopHostnameVerifier类:  作为主机名验证工具，实质上关闭了主机名验证，它接受任何
            //有效的SSL会话并匹配到目标主机。
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();

    }
}
